package 反射.代理;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//1、代理的基本构成 
//-抽象角色：声明真实对象和代理对象的共同接口,这样可在任何使用真实对象的地方都可以使用代理对象。
//
//--代理角色：代理对象内部含有真实对象的引用，从而可以在任何时候操作真实对象。代理对象提供一个与真实对象相同的接口，以便可以在任何时候替代真实对象。
//代理对象通常在客户端调用传递给真实对象之前或之后，执行某个操作，而不是单纯地将调用传递给真实对象，同时，代理对象可以在执行真实对象操作时，
//附加其他的操作，相当于对真实对象进行封装。
//
//--真实角色：即为代理对象所代表的目标对象，代理角色所代表的真实对象，是我们最终要引用的对象。
//
//下图有三种角色：Subject抽象角色、RealSubject真实角色、Proxy代理角色。其中：--Subject角色负责定义RealSubject和Proxy角色应该实现
//的接口；--RealSubject角色用来真正完成业务服务功能；--Proxy角色负责将自身的request请求，调用RealSubject对应的request功能来实现业务
//功能，自己不真正做业务。
//
//2、静态代理
//例子：1
/*interface Subject {// 抽象角色   -->接口
	public void doSomething();
}

class RealSubject implements Subject {// 真实角色   -->接口的实现
	public void doSomething() {
		System.out.println("call doSomething()");
	}
}

class SubjectProxy implements Subject{// 代理角色   -->接口实现的调用
	Subject subimpl = new RealSubject();//代理模式的作用是：为其他对象提供一种代理以控制对这个对象的访问。

	public void doSomething() {
		System.out.println("before"); // 调用目标对象之前可以做相关操作
		subimpl.doSomething();
		System.out.println("after");// 调用目标对象之后可以做相关操作
	}
}

public class 代理 {
	public static void main(String[] args) throws Exception {
		Subject sub = new SubjectProxy();
		sub.doSomething();
	}
}*/
//可以看到，SubjectProxy实现了Subject接口（和RealSubject实现相同接口），并持有的是Subject接口类型的引用。这样调用的依然是doSomething方法，
//只是实例化对象的过程改变了，结果来看，代理类SubjectProxy可以自动为我们加上了before和after等我们需要的动作。
//
//如果将来需要实现一个新的接口，就需要在代理类里再写该接口的实现方法，对导致代理类的代码变得臃肿；另一方面，当需要改变抽象角色接口时，无疑真实角色和代
//理角色也需要改变。

/*
开发一个接口，包含两个方法，可以向指定的人问候“你好”或者“再见”。
例子：2
public interface IHello {
	void sayHello(String name);
	void sayGoogBye(String name);
}
创建一个简单的类，实现这个IHello接口。

public class Helloimplements implements IHello {
	@Override
	public void sayHello(String name) {
      System.out.println("Hello " + name);
	}
	@Override
	public void sayGoogBye(String name) {
      System.out.println(name+" GoodBye!");
	}
}
消费这个实现类，迄今为止没什么特别的。
现在假设我们接到了这个需求：老板要求在该实现类每次问候某人时，必须把问候的细节记录到日志文件里。为了简单起见，我们在问候前打印
下面的一行语句来模拟日志记录的动作。

System.out.println("问候之前的日志记录...");

您也许会说，这还不简单？直接修改Helloimplements的对应方法，把这行日志插入到对应方法即可：

public class Helloimplements implements IHello {
  @Override
  public void sayHello(String name) {
  	System.out.println("问候之前的日志记录...");
      System.out.println("Hello " + name);
  }
  @Override
  public void sayGoogBye(String name) {
  	System.out.println("问候之前的日志记录...");
      System.out.println(name+" GoodBye!");
  }
}

然而，老板的要求是：不允许你修改原来的Helloimplements类。在现实场景中，Helloimplements可能是第三方的jar包提供的，我们没有办法修改代码。

您也许会说，我们可以用设计模式里的代理模式，即创建一个新的Java类作为代理类，同样实现IHello接口，然后将Helloimplements类的实例传入代理类。
我们虽然被要求不允许修改Helloimplements的代码，但是可以把日志记录代码写在代理类里。完整代码如下：
*/
/*
interface IHello {
	void sayHello(String name);
	void sayGoodBye(String name);
}

class Helloimplements implements IHello {
	@Override
	public void sayHello(String name) {
		System.out.println("Hello " + name);
	}

	@Override
	public void sayGoodBye(String name) {
		System.out.println(name + " GoodBye!");
	}
}

public class 静态代理 implements IHello {// StaticProxy
	private IHello iHello;

//	public void setImpl(IHello impl) {
//		this.iHello = impl;
//	}
	public 静态代理(IHello iHello) {
		this.iHello=iHello;
	}
	
	@Override
	public void sayHello(String name) {
		System.out.println("问候之前的日志记录...");
		iHello.sayHello(name);
	}

	@Override
	public void sayGoodBye(String name) {
		System.out.println("问候之前的日志记录...");
		iHello.sayGoodBye(name);
	}

	static public void main(String[] arg) {
		//Helloimplements hello = new Helloimplements();
		//proxy.setImpl(hello);
		静态代理 proxy = new 静态代理(new Helloimplements());
		proxy.sayHello("Jerry");
		proxy.sayGoodBye("Jerry");
	}
}
*/

//静态代理总结: 
//1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.
//2.缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
//如何解决静态代理中的缺点呢?答案是可以使用动态代理方式: 
//动态代理有以下特点: 
// 1.代理对象,不需要实现接口  目标对象还是要实现接口
// 2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
// 3.动态代理也叫做:JDK代理,接口代理

